package util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

import shamir.shamirKey;
import sun.misc.BASE64Encoder;

public class Utils
{
    public static byte[] concat( byte[] arr1, byte[] arr2 )
    {
        byte[] C = new byte[ arr1.length + arr2.length ];
        System.arraycopy( arr1, 0, C, 0, arr1.length );
        System.arraycopy( arr2, 0, C, arr1.length, arr2.length );
        return C;
    }

    public static BigInteger readModulus( String keyFileName ) throws IOException
    {
        InputStream in = new FileInputStream( keyFileName );
        ObjectInputStream oin = new ObjectInputStream( new BufferedInputStream( in ) );
        try
        {
            BigInteger m = (BigInteger)oin.readObject();
            return m;
        }
        catch( Exception e )
        {
            throw new RuntimeException( "Niepoprawny klucz publiczny", e );
        }
        finally
        {
            oin.close();
        }
    }

    public static PrivateKey findPrivate( BigInteger m, String exp )
    {
        try
        {
            BigInteger e = new BigInteger( exp );
            System.out.println( "findPrivate: m " + m );
            System.out.println( "findPrivate: exp " + exp );
            RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec( m, e );
            KeyFactory fact = KeyFactory.getInstance( "RSA" );
            PrivateKey privKey = fact.generatePrivate( keySpec );
            return privKey;
        }
        catch( Exception e )
        {
            throw new RuntimeException( "Niepoprawny klucz prywatny", e );
        }
    }

    public static PublicKey readPublicKey( String keyFileName ) throws IOException
    {
        InputStream in = new FileInputStream( keyFileName );
        ObjectInputStream oin = new ObjectInputStream( new BufferedInputStream( in ) );
        try
        {
            BigInteger m = (BigInteger)oin.readObject();
            BigInteger e = (BigInteger)oin.readObject();
            RSAPublicKeySpec keySpec = new RSAPublicKeySpec( m, e );
            KeyFactory fact = KeyFactory.getInstance( "RSA" );
            PublicKey pubKey = fact.generatePublic( keySpec );
            return pubKey;
        }
        catch( Exception e )
        {
            throw new RuntimeException( "Niepoprawny klucz publiczny", e );
        }
        finally
        {
            oin.close();
        }
    }

    public static PrivateKey readPrivateKey( String keyFileName ) throws IOException
    {
        InputStream in = new FileInputStream( keyFileName );
        ObjectInputStream oin = new ObjectInputStream( new BufferedInputStream( in ) );
        try
        {
            BigInteger m = (BigInteger)oin.readObject();
            BigInteger e = (BigInteger)oin.readObject();
            RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec( m, e );
            KeyFactory fact = KeyFactory.getInstance( "RSA" );
            PrivateKey privKey = fact.generatePrivate( keySpec );
            return privKey;
        }
        catch( Exception e )
        {
            throw new RuntimeException( "Niepoprawny klucz prywatny", e );
        }
        finally
        {
            oin.close();
        }
    }

    public static void saveToFile( String fileName, BigInteger mod, BigInteger exp ) throws IOException
    {
        ObjectOutputStream oout = new ObjectOutputStream( new BufferedOutputStream( new FileOutputStream( fileName ) ) );
        try
        {
            System.out.println( "saveToFile: exponent fileName: " + fileName + " -> " + exp );
            System.out.println( "saveToFile: modulus fileName: " + fileName + " -> " + mod );
            oout.writeObject( mod );
            oout.writeObject( exp );
        }
        catch( Exception e )
        {
            throw new IOException( "Unexpected error", e );
        }
        finally
        {
            oout.close();
        }
    }

    public static void saveShamirKey( String fileName, shamirKey key ) throws IOException
    {
        ObjectOutputStream oout = new ObjectOutputStream( new BufferedOutputStream( new FileOutputStream( fileName ) ) );
        try
        {
            // System.out.println(exp);
            oout.writeObject( key.getF() );
            oout.writeObject( key.getP() );
            oout.writeObject( key.getX() );
        }
        catch( Exception e )
        {
            throw new IOException( "Unexpected error", e );
        }
        finally
        {
            oout.close();
        }
    }

    public static shamirKey readShamirKey( String keyFileName ) throws IOException
    {
        InputStream in = new FileInputStream( keyFileName );
        ObjectInputStream oin = new ObjectInputStream( new BufferedInputStream( in ) );
        try
        {
            BigInteger f = (BigInteger)oin.readObject();
            BigInteger p = (BigInteger)oin.readObject();
            BigInteger x = (BigInteger)oin.readObject();
            shamirKey shamir = new shamirKey();
            shamir.setF( f );
            shamir.setP( p );
            shamir.setX( x );
            return shamir;
        }
        catch( Exception e )
        {
            throw new RuntimeException( "", e );
        }
        finally
        {
            oin.close();
        }
    }

    public static String signSHA( String code )
    {
        try
        {
            MessageDigest md = MessageDigest.getInstance( "SHA" );
            BASE64Encoder encoder = new BASE64Encoder();
            String encoded = encoder.encode( md.digest( code.getBytes() ) );
            return encoded;
        }
        catch( Exception e )
        {
            return null;
        }
    }
}
